import be.kuleuven.cs.som.annotate.*;

/**
 * A class of squares involving a temperature and a humidity.
 * 
 * @invar	The temperature of each square must be a valid temperature
 * 			for that square.
 * 			| canHaveAsTemperature(getTemparture(Temperature.CELCIUS))
 * @invar 	The humidity of each square must be a valid humidity
 * 			for a square.
 * 			| isValidHumidity(getHumidity());
 * 
 * @version	1.0
 * @author	Dries Liebens 
 * 			Loic Vercruysse
 */
public class Square {

	/**
	 * Initialize this new square with a temperature equal to 20 degrees Celcius
	 * and a humidity of 0%.
	 * 
	 * @effect	The new square is initialized with a temperature of 20 degrees Celcius.
	 * 			and a humidity of 0%.
	 * 			| this(100,Temperature.CELCIUS,0)
	 */
	public Square() {
		this(20,Temperature.CELCIUS,0);
	}

	/**
	 * Initialize this new square with given temperature and given humidity.
	 * 
	 * @param	temperature
	 * 			The temperature for this new square in the preferred scale.
	 * @param	humidity
	 * 			The humidity for this new square.
	 * @param	scale
	 * 			The scale in which the temperature is expressed in.
	 * @pre		The given temperature must be a valid temperature.
	 * 			| canHaveAsTemperature(temperature)
	 * @pre		The given humidity must be a valid humidity.
	 * 			| isValidHumidity(humidity)
	 * @post	The temperature of this new square is equal to the given temperature.
	 * 			| new.getTemperature() == temperature
	 * @post	The humidity if this new square is equal to the given humidity.
	 * 			| new.getHumidity() == humidity
	 * @throws	IllegalArgumentException
	 * 			The given temperature is not a valid temperature for this square.
	 * 			| !canHaveAsTemperature(temperature)
	 */
	public Square(double temperature, Temperature scale, int humidity) throws IllegalArgumentException {
		setHumidity(humidity);
		this.maximumTemperature = 5000;
		this.minimumTemperature = -200;
		setTemperature(temperature,scale);
		// TODO zie ook OilTank van in les voor @Raw etc te doen
		// TODO extra uitleg bij wat het doet
		// TODO te initialisren = thresholdHeatDamageTemperature
		// TODO verschil canHaveAs en isValid
	}	
	
	/**
	 * Returns the temperature in the preferred scale of this square.
	 * 
	 * @param	scale
	 * 			The scale in which the temperature is expressed in.
	 */
	@Basic
	public double getTemperature(Temperature scale) {
		return Temperature.CELCIUS.convertTo(this.temperature, scale);
	}
	
	/**
	 * Sets the given temperature as the temperature.
	 * 
	 * @param	temperature
	 * 			The temperature in the preferred scale to set.
	 * @param	scale
	 * 			The scale in which the temperature is expressed in.
	 * @post	The temperature is the same as the give temperature.
	 * 			| new.getTemperature(scale) == temperature
	 * @throws	IllegalArgumentException
	 * 			The temperature is not valid.
	 * 			| !isValidTemperature(temperature,scale)
	 */
	public void setTemperature(double temperature,Temperature scale) throws IllegalArgumentException{
		if(!canHaveAsTemperature(temperature,scale))
			throw new IllegalArgumentException("Non-valid temperature!");
		this.temperature = scale.convertTo(temperature, Temperature.CELCIUS);
	}	
	
	/**
	 * Checks whether this square can have the given temperature as temperature.
	 * 
	 * @param	temperature
	 * 			The temperature in the preferred to check.
	 * @param	scale
	 * 			The scale in which the temperature is expressed in.
	 * @return	True if the temperature is higher or equal to the minimum temperature
	 * 			and lower or equal to the maximum temperature.
	 * 			| result == (temperature <= getMaximumTemperature(scale))
	 * 			|				&& (temperature >= getMinimumTemperature(scale))
	 */
	public boolean canHaveAsTemperature(double temperature,Temperature scale){
		return ( (temperature <= this.getMaximumTemperature(scale)) 
					&& (temperature >= this.getMinimumTemperature(scale)) );
	}
	
	/**
	 * Variable referencing the temperature in degrees Celcius of this square.
	 */
	private double temperature;
	
	/**
	 * Returns the maximum temperature in the preferred scale of this square.
	 * 
	 * @param	scale
	 * 			The scale in which the temperature is expressed in.
	 */
	@Basic
	public double getMaximumTemperature(Temperature scale){
		return Temperature.CELCIUS.convertTo(maximumTemperature, scale);
	}
	
	/**
	 * Sets the given temperature as maximum temperature.
	 * 
	 * @param	maximumTemperature
	 * 			The maximum temperature in the preferred scale to set.
	 * @param	scale
	 * 			The scale in which the temperature is expressed in.
	 * @post	The maximum temperature is the same as the given maximum temperature.
	 * 			| new.getMaximumTemperature(scale) == maximumTemperature
	 * @throws	IllegalArgumentException
	 * 			The maximum temperature is not valid.
	 * 			| !isValidMaximumTemperature(maximumTemperature,scale)
	 */
	public void setMaximumTemperature(double maximumTemperature,Temperature scale) throws IllegalArgumentException{
		if(!canHaveAsMaximumTemperature(maximumTemperature,scale))
			throw new IllegalArgumentException("Non-valid maximum temperature!");
		this.maximumTemperature = scale.convertTo(maximumTemperature, Temperature.CELCIUS);
	}
	
	/**
	 * Checks whether this square can have the given temperature as maximum temperature.
	 * 
	 * @param	maximumTemperature
	 * 			The temperature in the preferred scale to check.
	 * @param	scale
	 * 			The scale in which the temperature is expressed in.
	 * @return	True if maximumTemperature is higher or equal then the minimum temperature
	 * 			and maximumTemperature is higher or equal then the current temperature
	 * 			| result == ( (maximumTemperature >= getMinimumTemperature(scale)) 
	 * 			|				&& (maximumTemperature >= getTemperature(scale)) )
	 */
	public boolean canHaveAsMaximumTemperature(double maximumTemperature,Temperature scale){
		return ( (maximumTemperature >= getMinimumTemperature(scale)) 
					&& (maximumTemperature >= getTemperature(scale)) );
	}
	
	/**
	 * Variable referencing the maximum temperature in degrees Celcius of this square.
	 */
	private double maximumTemperature;
	
	/**
	 * Returns the minimum temperature in the preferred scale of this square.
	 * 
	 * @param	scale
	 * 			The scale in which the temperature is expressed in.
	 */
	@Basic
	public double getMinimumTemperature(Temperature scale){
		return Temperature.CELCIUS.convertTo(minimumTemperature,scale);
	}

	/**
	 * Sets the given temperature as minimum temperature.
	 * 
	 * @param	minimumTemperature
	 * 			The minimum temperature in the preferred scale to set.
	 * @param	scale
	 * 			The scale in which the temperature is expressed in.
	 * @post	The minimum temperature is the same as the given minimum temperature.
	 * 			| new.getMinimumTemperature(scale) == minimumTemperature
	 * @throws	IllegalArgumentException
	 * 			The minimum temperature is not valid.
	 * 			| !isValidMinimumTemperature(minimumTemperature,scale)
	 */
	public void setMinimumTemperature(double minimumTemperature,Temperature scale) throws IllegalArgumentException{
		if(!canHaveAsMinimumTemperature(minimumTemperature,scale))
			throw new IllegalArgumentException("Non-valid minimum temperature!");
		this.minimumTemperature = scale.convertTo(minimumTemperature,Temperature.CELCIUS);
	}
	
	/**
	 * Checks whether this square can have the given temperature as minimum temperature.
	 * 
	 * @param	minimumTemperature
	 * 			The temperature in the preferred scale to check.
	 * @param	scale
	 * 			The scale in which the temperature is expressed in.
	 * @return	True if minimumTemperature is lower or equal then the maximum temperature
	 * 			and minimumTemperature is lower or equal then the current temperature
	 * 			| result == ( (minimumTemperature <= getMaximumTemperature(scale)) 
	 * 			|				&& (minimumTemperature <= getTemperature(scale)) )
	 */
	public boolean canHaveAsMinimumTemperature(double minimumTemperature,Temperature scale){
		return ( (minimumTemperature <= getMaximumTemperature(scale)) && (minimumTemperature <= getTemperature(scale)) );
	}

	/**
	 * Variable referencing the minimum temperature in degrees Celcius of this square.
	 */
	private double minimumTemperature;

	/**
	 * Returns the humidity in percents of this square.
	 */
	@Basic
	public float getHumidity() {
		return humidity;
	}
	
	/**
	 * Sets the given humidity as humidity of this square.
	 * 
	 * @param	humidity
	 * 			The humidity to set.
	 * @pre		The given humidity must be a valid percentage.
	 * 			| isValidHumidity(humidity)
	 * @post	The humidity of this square is equal to the given humidity/
	 * 			| new.getHumidity() == humidity
	 */
	public void setHumidity(float humidity) {
		assert isValidHumidity(humidity);
		this.humidity = humidity;
	}
	
	/**
	 * Checks whether this square can have the given humidity as humidity.
	 * 
	 * @param	humidity
	 * 			The humidity to check.
	 * @return	True if humidity is equal or lower then 100 and humidity is equal or higher then 0,
	 * 			humidity may also have maximum 2 numbers after the comma.
	 * 			| result == ( (humidity >= 0) && (humidity <= 100) 
	 * 			|				&& (humidity % 0.01 == 0) )
	 */
	public static boolean isValidHumidity(float humidity) {
		return ( (humidity >= 0) && (humidity <= 100) && (humidity%0.01 == 0) );
	}
	
	/**
	 * Variable referencing the humidity of this square.
	 */
	private float humidity; 
	
	/**
	 * Returns the threshold for heat damage in the preferred scale for squares.
	 * 
	 * @param	scale
	 * 			The scale in which the temperature is expressed in.
	 */
	@Basic
	public static double getThresholdHeatDamageTemperature(Temperature scale) {
		return Temperature.CELCIUS.convertTo(thresholdHeatDamageTemperature,scale);
	}
	
	/**
	 * Sets the threshold for heat damage in the preferred scale for squares.
	 * 
	 * @param	thresholdHeatDamageTemperature
	 * 			The temperature to set as threshold for heat damage in the preferred scale.
	 * @param	scale
	 * 			The scale in which the temperature is expressed in.
	 * @post	The threshold for heat damage for squares is equal to the given temperature.
	 * 			new.getThresholdHeatDamageTemperature(scale) == thresholdHeatDamageTemperature
	 * @throws	IllegalArgumentException
	 * 			The given temperature is not valid.
	 * 			| !isValidThresholdHeatDamageTemperature(thresholdHeatDamageTemperature,scale)
	 */
	public static void setThreadholdHeatDamageTemperature(int thresholdHeatDamageTemperature,Temperature scale) throws IllegalArgumentException {
		assert isValidThresholdHeatDamageTemperature(thresholdHeatDamageTemperature,scale);
		Square.thresholdHeatDamageTemperature = scale.convertTo(thresholdHeatDamageTemperature, Temperature.CELCIUS);
	}
	
	/**
	 * Checks whether this square can have the given thresholdHeatDamageTemperature as thresholdHeatDamageTemperature.
	 * 
	 * @param	thresholdHeatDamageTemperature
	 * 			The temperature in the preferred scale to check.
	 * @param	scale
	 * 			The scale in which the temperature is expressed in. 
	 * @return	True if thresholdHeatDamageTemperature is higher then the threshold for cold damage.
	 * 			| result == (thresholdHeatDamageTemperature > getThresholdColdDamageTemperature(scale))
	 */
	public static boolean isValidThresholdHeatDamageTemperature(int thresholdHeatDamageTemperature,Temperature scale) {
		return thresholdHeatDamageTemperature > getThresholdColdDamageTemperature(scale);
	}
	
	/**
	 * Variable referencing the threshold for heat damage in degrees Celcius this square.
	 */
	private static double thresholdHeatDamageTemperature;
	
	/**
	 * Returns the threshold for cold damage in the preferred scale for squares.
	 * 
	 * @param	scale
	 * 			The scale in which the temperature is expressed in.
	 */
	@Basic @Immutable
	public static double getThresholdColdDamageTemperature(Temperature scale) {
		return Temperature.CELCIUS.convertTo(thresholdColdDamageTemperature, scale);
	}
	
	/**
	 * Variable referencing the threshold for cold damage in degrees Celcius for squares.
	 */
	public static final double thresholdColdDamageTemperature = -5;
	
	
	
	// *****************************************************************************************************//
	//	goed tot hier - evt nog nakijken of correctheid, tags, spellingsfouten, ...							//
	// *****************************************************************************************************//
	
	/**
	 * Calculates the heat damage due the temperature of this square.
	 * 
	 * @return	zero if there is no heat damage.
	 * 			| if(getTemperature(Temperature.CELCIUS) <= getThresholdHeatDamageTemperature(Temperature.CELCIUS))
	 * 			|	then result == 0;
	 * @return 	... TODO
	 * 			| if(getTemperature(Temperature.CELCIUS) > getThresholdHeatDamageTemperature(Temperature.CELCIUS))
	 * 			|	then result == 
	 * 			|	(Math.abs(getTemperature(Temperature.CELCIUS))
	 * 			|		- Math.abs(getThresholdHeatDamageTemperature(Temperature.CELCIUS)))/15
	 */
	public int calculateHeatDamage() {
		if(getTemperature(Temperature.CELCIUS) <= getThresholdHeatDamageTemperature(Temperature.CELCIUS))
			return 0;
		Double heatDamage = (getTemperature(Temperature.CELCIUS) - getThresholdHeatDamageTemperature(Temperature.CELCIUS))/15; // TODO verandert 15 ook?
		if( (heatDamage % 1) != 0)
			heatDamage = (heatDamage / 1) + 1;
		return heatDamage.intValue();
	}
	
	/**
	 * Calculates the cold damage due the temperature of this square.
	 * 
	 * @return	zero if there is no cold damage.
	 * 			| if(getTemperature(Temperature.CELCIUS) >= -5)
	 * 			|	then result == 0;
	 * @return	... TODO
	 * 			| if(getTemperature(Temperature.CELCIUS) < -5)
	 * 			|	then result == 
	 * 			|	( Math.abs(getTemperature(Temperature.CELCIUS)
	 * 			|		- Math.abs(getThresholdColdDamageTemperature(Temperature.CELCIUS)))/10)
	 */
	public int calculateColdDamage() {
		if(getTemperature(Temperature.CELCIUS) >= getThresholdColdDamageTemperature(Temperature.CELCIUS))
			return 0;
		Double coldDamage =  (Math.abs(getTemperature(Temperature.CELCIUS)- Math.abs(getThresholdColdDamageTemperature(Temperature.CELCIUS)))/10);
		return coldDamage.intValue();
	}

	
	// *****************************************************************************************************//
	//	@work tot hier - hieronder enkel wat klad over opdracht												//
	// *****************************************************************************************************//

	

	// glad = defensief
	
	public void setMadeOfSlipperyMaterial(boolean madeOfSlipperyMaterial) {
		this.madeOfSlipperyMaterial = madeOfSlipperyMaterial;
	}
	
	/**
	 * Returns if this square is made of slippery material.
	 */
	@Basic
	public boolean isMadeOfSlipperyMaterial() {
		return madeOfSlipperyMaterial;
	}

	/**
	 * Variable referencing if this square is made of slippery material.
	 */
	private boolean madeOfSlipperyMaterial;
	
	
	
	
	public float calculateRustDamage() {
		return (getHumidity() - 30)/7;
	}
	
	
	
	
	private int[] borders;
	
	
	
	
//	public boolean getSlippery() {
//		return isSlippery();
//	}
//	private boolean isSlippery(){
//		if(getIfItIsMadeOfSlipperyMaterial())
//			return true;
//		if((getTemperature() > 273) || (getHumidity() == 100))
//			return true;
//		if((getTemperature() <= 273) || (getHumidity() > 10) )
//			return true;
//		return false;
//	}

	public double getInhabitability() {
		return calculateInhabitability();
	}
	private double calculateInhabitability(){
		double a = Math.sqrt(calculateHeatDamage() * calculateHeatDamage()
				* calculateHeatDamage());
		double b = Math.sqrt(101 - getHumidity());
		double c = Math.sqrt(calculateColdDamage());
		return -(a / b) - c;
	}

	
	
	public void mergeWith() { // andere cel of grens meegeven

	}

	
	// borders uitwerken --> totaal
	public Square getSomething(int border) { // TODO naam etc

		// vragen aan prof

		return null;
	}

	// TODO list
	/*
	 * deze klasse : alles testklasse hoofdklasse
	 */
}
